    var renderer;//渲染器
    var camera; //摄像头                    
    var scene; //创建场景，后续元素需要加入到场景中才会显示出来
    var width;//页面宽度
    var height;//页面高度
    var light; //光源
    var origPoint = new THREE.Vector3(0, 0, 0);//原点
    var cubeSize = 50;
    var originWidth,originHeight;

    initRenderer();
    initCamera();
    initScene();
    initLight();
    initObject();
    initEvents();

    render();
    
    function initRenderer() {
        width = window.innerWidth;
        height = window.innerHeight;
        renderer = new THREE.WebGLRenderer({
            antialias : true //抗锯齿开启
        });

        renderer.setSize(width, height); //设置渲染器宽度和高度
        renderer.setClearColor(0xFFFFFF, 1.0); //设置背景颜色
        renderer.setPixelRatio(window.devicePixelRatio);//设置设备像素比
        document.getElementById('canvas-frame').appendChild(renderer.domElement);//把渲染器放置到页面中
    }

    function initCamera() {
        var fov = 60;
        var aspect = window.innerWidth / window.innerHeight;

        camera = new THREE.PerspectiveCamera(fov,aspect, 1, 1000);
        camera.position.z = 500;

        //fov 为摄像头垂直可视角度
        originHeight = camera.position.z * Math.tan(fov/360 * Math.PI) * 2;
        originWidth = originHeight * aspect;
    }

    function initScene() {
        scene = new THREE.Scene();
    }

    function initLight() {
        //环境光
        ambientLight = new THREE.AmbientLight( 0xfefefe );
        scene.add(ambientLight);
    }

    function createCubeGroup(groupName,cubeName,size){
        var colors = ['#ff6b02', '#dd422f',
                      '#ffffff', '#fdcd02',
                      '#3d81f7', '#019d53'];
        
        var faces = [];

        colors.forEach(color=>{
            var canvas = document.createElement("canvas");
            var context = canvas.getContext("2d");
            canvas.width = 256;
            canvas.height = 256;

            //画一个全黑的正方形
            context.fillStyle = 'rgba(0,0,0,1)';
            context.fillRect(0, 0, 256, 256);
            
            //再画一个圆角矩形
            context.rect(16, 16, 224, 224);
            context.lineJoin = 'round';
            context.lineWidth = 16;
            context.fillStyle = color;
            context.strokeStyle = color;
            context.stroke();
            context.fill();

            faces.push(canvas);
        });

        var group = new THREE.Group();
        group.name = groupName;

        //画27个小方块
        for(var i=-1;i<2;i++) {
            for(var j=-1;j<2;j++){
                for(var z=-1;z<2;z++){
                    var materials = [];

                    faces.forEach(face=>{
                        var texture = new THREE.Texture(face);
                        texture.needsUpdate = true;

                        materials.push(new THREE.MeshLambertMaterial({ map: texture}));
                    });

                    var cube = createCube(size,materials);

                    cube.position.x = i*size;
                    cube.position.y = j*size;
                    cube.position.z = z*size;
                    cube.name = cubeName;

                    cube.originPosition = new THREE.Vector3(i*size,j*size,z*size);

                    group.add(cube);
                }
            }
        }

        return group;
    }
    
    function initObject() {
        var group = createCubeGroup("cubeGroup","cube",cubeSize);

        //外层套一个透明大方块，以便后续确认触摸魔方面
        var cubemat = new THREE.MeshLambertMaterial({ color: 0xff0000 , opacity: 0, transparent: true});
        // var cubemat = new THREE.MeshBasicMaterial({ opacity: 0, transparent: true });
        var coverCube = createCube(cubeSize*3,cubemat);
        coverCube.name = "cover";

        group.add(coverCube);

        //添加x,y,z轴
        var lineSize = 300;
        var xpoint = new THREE.Vector3(lineSize,0,0);
        var ypoint = new THREE.Vector3(0,lineSize,0);
        var zpoint = new THREE.Vector3(0,0,lineSize);

        //iphone 6/7/8 plus 无法显示线条，用iphone 6/7/8测试即可
        group.add(createAxis(origPoint,xpoint,0x0000ff)); //蓝色
        group.add(createAxis(origPoint,ypoint,0x00ff00)); //绿色
        group.add(createAxis(origPoint,zpoint,0xff0000)); //红色

        group.rotateY(45 / 180 * Math.PI);

        //这里axis是group自有坐标系
        group.rotateOnAxis(new THREE.Vector3(1, 0, 1), 25 / 180 * Math.PI);

        scene.add(group);

        //添加按钮
        addButton("disorganize" , "./images/disorganize-btn.jpg", 10, 10);
        addButton("reset" , "./images/reset-btn.jpg", 60, 10);
        addButton("save" , "./images/save-btn.jpg", 110, 10);
        addButton("restore" , "./images/restore-btn.jpg", 160, 10);
    }

    function addButton(name, imageUrl, left, top){
        var loader = new THREE.TextureLoader();

        loader.load(imageUrl, function (texture) {
            // console.log(texture);
            var size = 40;

            var geometry = new THREE.PlaneBufferGeometry(size, size);
            var material = new THREE.MeshBasicMaterial({ map: texture});

            var plane = new THREE.Mesh(geometry, material);
            plane.name = name;
            plane.clickable = true;
            plane.position.set(left + size/2 - originWidth/2,originHeight/2 - size/2 - top, 0);

            scene.add(plane);

            render();
        });
    }

    function createCube(size,mat) {
        var geo = new THREE.BoxGeometry(size, size, size);
        var cube = new THREE.Mesh(geo, mat);

        return cube;
    }

    function createAxis(start,end,color) {
        var mat = new THREE.LineBasicMaterial({ color: color });
        var geo = new THREE.Geometry();
        geo.vertices.push(start, end);

        var line = new THREE.Line(geo, mat);

        return line;
    }

    function render() {
        renderer.clear();
        renderer.render( scene, camera );
    }

    function getIntersect(touch){
        //将屏幕坐标转换为[-1,1]
        var point = new THREE.Vector2();
        point.x = touch.clientX*2/window.innerWidth - 1;
        point.y = 1 - touch.clientY*2/window.innerHeight

        // console.log(point);

        var raycaster = new THREE.Raycaster();

        raycaster.setFromCamera(point, camera);

        var group = null;
        var btn = null;
        
        scene.children.forEach(c=>{
            if(c.name == "cubeGroup") {
                group = c;
            }
        });

        if(group!=null) {
            var intersects = raycaster.intersectObjects(group.children);
            var normal,cube;

            // console.log(intersects);
            intersects.forEach(item=>{
                if(item.object.name == "cover") {
                    normal = item.face.normal;
                }
                else if(item.object.name=="cube" && cube==null){
                    cube = item.object;
                }
            });
            
            //是否点击按钮
            raycaster.intersectObjects(scene.children).forEach(item=>{
                console.log(item);

                if(item.object.clickable){
                    btn = item.object.name;
                }
            });
        }

        return {
            group : group,
            normal : normal,
            cube : cube,
            btn : btn
        }
    }
   

    function initEvents(){
        var startCube = null;
        var startNormal = null;
        var startPoint = null;
        var rotating = false;
        var wholeRotate = false;

        window.ontouchstart = (event)=>{
            var touch = event.touches[0];

            var intersect = getIntersect(touch);
            startPoint = new THREE.Vector2(touch.clientX,touch.clientY);
            startCube = intersect.cube;
            startNormal = intersect.normal;

            if(startCube!=null) {
                console.log("起始点" + rotating);
                console.log(startCube.position);
                // console.log(startCube);
            }
            else if(intersect.btn!=null){
                if(intersect.btn=="disorganize"){
                    disorganize();
                }
                else if(intersect.btn=="reset"){
                    resetScene();
                }
                else if(intersect.btn=="save"){
                    saveScene();
                }
                else if(intersect.btn=="restore"){
                    restoreScene();
                }
            }
        }

        window.ontouchmove= (event)=>{
            if(rotating){
                return;
            }
            
            var touch = event.touches[0];

            var intersect = getIntersect(touch);

            if(startCube!=null && intersect.cube!=null){
                console.log("单面旋转");
                rotateFace(intersect);
            }
            else{
                rotateAll(touch,intersect);
            }
        }

        window.ontouchend = (event)=>{
            startCube = null;
        }

        function rotateAll(touch,intersect){
            //两点距离超过10
            var movePoint = new THREE.Vector2(touch.clientX,touch.clientY);

            var distance = getDistance(startPoint,movePoint);

            console.log("distance=" + distance);

            if(distance>10){
                rotating = true;

                var rotateNodes = intersect.group.children.filter((c)=>{
                    return c.name=="cube";
                });
                
                var rotateAxis = 0;
                var rotateAngle = 0;
                
                var xLine = new THREE.Vector3(1, 0, 0);
                var yLine = new THREE.Vector3(0, 1, 0);
                var zLine = new THREE.Vector3(0, 0, 1);

                if(Math.abs(startPoint.y - movePoint.y)<10){
                    console.log("绕y轴方向旋转");
                    rotateAxis = yLine;

                    if(movePoint.x > startPoint.x){
                        rotateAngle = 90;
                    }
                    else{
                        rotateAngle = -90;
                    }
                }
                else if(movePoint.x<window.innerWidth/2){
                    console.log("绕z轴方向旋转");
                    rotateAxis = zLine;

                    if(movePoint.y > startPoint.y){
                        rotateAngle = 90;
                    }
                    else{
                        rotateAngle = -90;
                    }
                }
                else{
                    console.log("绕x轴方向旋转");
                    rotateAxis = xLine;

                    if(movePoint.y > startPoint.y){
                        rotateAngle = 90;
                    }
                    else{
                        rotateAngle = -90;
                    }
                }

                rotateCube(rotateNodes, rotateAxis, rotateAngle ,3);
            }
        }

        function getDistance(pt1,pt2){
            return Math.sqrt(Math.pow(pt1.x - pt2.x,2) + Math.pow(pt1.y - pt2.y,2));
        }

        function rotateFace(intersect){                
            var startPos = startCube.position;
            var endCube = intersect.cube;

            var endPos = endCube.position;
            var vec = new THREE.Vector3(endCube.position.x,endCube.position.y,endCube.position.z);

            if(!startPos.equals(endPos)) {               
                console.log("滑动点");
                console.log(endPos);

                //根据法线可以知道触摸的是那一面
                
                //根据触摸两个方块的位置可以判断旋转方向和所在面

                //注意sub后会改变变量本身
                vec.sub(startPos);

                // var vec = new THREE.Vector3(0,0,0);
                console.log("方向");
                console.log(vec);

                var direction = "";

                if(vec.x ==0 && vec.y ==0) {
                    // 往z轴正方向滑动
                    direction = vec.z > 0 ? "z" : "-z";
                }
                else if(vec.y ==0 && vec.z ==0) {
                    // 往x轴正方向滑动
                    direction = vec.x >0 ? "x" : "-x";
                }
                else if(vec.x ==0 && vec.z ==0) {
                    // 往y轴正方向滑动
                    direction = vec.y >0 ? "y" : "-y";
                }

                console.log(direction);
                
                // 旋转后group后，position是否发生变化->不会

                var xLine = new THREE.Vector3(1, 0, 0);
                var xLineAd = new THREE.Vector3(-1, 0, 0);
                var yLine = new THREE.Vector3(0, 1, 0);
                var yLineAd = new THREE.Vector3(0, -1, 0);
                var zLine = new THREE.Vector3(0, 0, 1);
                var zLineAd = new THREE.Vector3(0, 0, -1);

                var rotateNodes = [];
                var rotateAxis = null;
                var rotateAngle = 0;
                                   
                //目前只考虑三个面接触xLineAd,zLine,yLine
                if(startNormal.equals(xLineAd)){
                    //接触面为x轴负方向垂直面,这一层x值都相同
                    
                    //z值相同,y轴方向滑动
                    if(startPos.z==endPos.z) {
                        //转动方块
                        rotateNodes = intersect.group.children.filter((c)=>{
                            return c.name=="cube" && c.position.z == startPos.z
                        });

                        //转动轴
                        rotateAxis = zLineAd;

                        if(direction == "y"){
                            rotateAngle = 90;
                            console.log("绕z轴顺时针转动");
                        }
                        else{
                            rotateAngle = -90;
                            console.log("绕z轴逆时针转动");
                        }
                    }
                    //y值相同,z轴方向滑动
                    else if(startPos.y==endPos.y){
                        rotateNodes = intersect.group.children.filter((c)=>{
                            return c.name=="cube" && c.position.y == startPos.y
                        });

                        rotateAxis = yLineAd;

                        if(direction=="z"){
                            rotateAngle = -90;
                            console.log("绕y轴逆时针转动");                        
                        }
                        else{
                            rotateAngle = 90;
                            console.log("绕y轴顺时针转动");
                        }
                    }
                }
                else if(startNormal.equals(zLine)){
                    //接触面为z轴正方向垂直面,这一层z值都相同
                    
                    //y值相同,x轴方向滑动
                    if(startPos.y==endPos.y){                            
                        rotateNodes = intersect.group.children.filter((c)=>{
                            return c.name=="cube" && c.position.y == startPos.y
                        });

                        rotateAxis = yLineAd;

                        if(direction=="x"){
                            rotateAngle = -90;
                            console.log("绕y轴逆时针转动");
                        }
                        else{
                            rotateAngle = 90;
                            console.log("绕y轴顺时针转动");
                        }
                    }
                    else if(startPos.x==endPos.x){    
                        rotateNodes = intersect.group.children.filter((c)=>{
                            return c.name=="cube" && c.position.x == startPos.x
                        });

                        rotateAxis = xLine;

                        //x值相同，y轴方向滑动
                        if(direction=="y"){
                            rotateAngle = -90;
                            console.log("绕x轴逆时针转动");
                        }
                        else{
                            rotateAngle = 90;
                            console.log("绕y轴顺时针转动");
                        }
                    }
                }
                else if(startNormal.equals(yLine)){
                    //接触面为y轴正方向垂直面,这一层y值都相同
                    
                    //z值相同,x轴方向滑动
                    if(startPos.z==endPos.z){
                        rotateNodes = intersect.group.children.filter((c)=>{
                            return c.name=="cube" && c.position.z == startPos.z
                        });

                        rotateAxis = zLineAd;

                        if(direction=="x"){
                            rotateAngle = 90;
                            console.log("绕z轴顺时针转动");
                        }
                        else{
                            rotateAngle = -90;
                            console.log("绕z轴逆时针转动");
                        }
                    }
                    //x值相同，z轴方向滑动
                    else if(startPos.x==endPos.x){
                        rotateNodes = intersect.group.children.filter((c)=>{
                            return c.name=="cube" && c.position.x == startPos.x
                        });

                        rotateAxis = xLine;

                        if(direction=="z"){
                            rotateAngle = 90;
                            console.log("绕x轴顺时针转动");
                        }
                        else{
                            rotateAngle = -90;
                            console.log("绕x轴逆时针转动");
                        }
                    }
                }

                if(rotateAxis!=null){
                    rotating = true;

                    rotateCube(rotateNodes, rotateAxis, rotateAngle ,3);

                    // setTimeout(()=>{
                    //     rotating = false;
                    // },5000);
                }
                else{
                    rotating = false;
                }
            }
        }

        function rotateCube(rotateNodes,rotateAxis,rotateAngle,perAngle,callback) {
            var interval = 2000;
            var currTime = new Date().getTime();
            
            if(rotateAngle<0){
                perAngle *= -1;
            }

            rotateCubeAnimate(rotateNodes,rotateAxis,rotateAngle,0,perAngle,callback);
        }
        
        function rotateCubeAnimate(rotateNodes,rotateAxis,rotateAngle,alreadyRotateAngle,perAngle,callback) {
            var rotateMatrix = rotateAroundWorldAxis(origPoint, rotateAxis, perAngle * Math.PI / 180);

            alreadyRotateAngle += perAngle;

            rotateNodes.forEach(cube=>{
                cube.applyMatrix(rotateMatrix);
                
                if(Math.abs(alreadyRotateAngle)==Math.abs(rotateAngle)){
                    cube.position.x = adjustValue(cube.position.x);
                    cube.position.y = adjustValue(cube.position.y);
                    cube.position.z = adjustValue(cube.position.z);
                }
            });

            render();

            if(Math.abs(alreadyRotateAngle) < Math.abs(rotateAngle)){
                // console.log(alreadyRotateAngle + "," + rotateAngle);
                requestAnimationFrame(()=>{
                    rotateCubeAnimate(rotateNodes,rotateAxis,rotateAngle,alreadyRotateAngle,perAngle,callback);
                });
            }
            else{
                console.log("旋转结束," + alreadyRotateAngle + "," + rotateAngle);
                rotating = false;
                
                if(callback!=null) {
                    console.log("callback");
                    callback();
                }
            }
        }

        function adjustValue(value){
            if(Math.abs(value)<0.1){
                return 0;
            }
            else if(Math.abs(Math.abs(value) - cubeSize)<0.1){
                if(value<0){
                    return -cubeSize;
                }
                else{
                    return cubeSize;
                }
            }
            else{
                return 0;
            }
        }

         /**
         * 绕过点p的向量vector旋转一定角度
         */
        function rotateAroundWorldAxis(p, vector, rad) {
            vector.normalize();
            var u = vector.x;
            var v = vector.y;
            var w = vector.z;

            var a = p.x;
            var b = p.y;
            var c = p.z;

            var matrix4 = new THREE.Matrix4();

            matrix4.set(u * u + (v * v + w * w) * Math.cos(rad), u * v * (1 - Math.cos(rad)) - w * Math.sin(rad), u * w * (1 - Math.cos(rad)) + v * Math.sin(rad), (a * (v * v + w * w) - u * (b * v + c * w)) * (1 - Math.cos(rad)) + (b * w - c * v) * Math.sin(rad),
            u * v * (1 - Math.cos(rad)) + w * Math.sin(rad), v * v + (u * u + w * w) * Math.cos(rad), v * w * (1 - Math.cos(rad)) - u * Math.sin(rad), (b * (u * u + w * w) - v * (a * u + c * w)) * (1 - Math.cos(rad)) + (c * u - a * w) * Math.sin(rad),
            u * w * (1 - Math.cos(rad)) - v * Math.sin(rad), v * w * (1 - Math.cos(rad)) + u * Math.sin(rad), w * w + (u * u + v * v) * Math.cos(rad), (c * (u * u + v * v) - w * (a * u + b * v)) * (1 - Math.cos(rad)) + (a * v - b * u) * Math.sin(rad),
            0, 0, 0, 1);

            return matrix4;
        }

        function rotateByAxisX(cubes,index,callback){            
            var xLine = new THREE.Vector3(1, 0, 0);

            var rotateCubes = cubes.filter((c)=>{
                return c.position.x == index * cubeSize
            });

            // console.log(rotateCubes);

            //一次转到位
            rotateCube(rotateCubes, xLine, 90 , 10,callback);
        }

        function rotateByAxisY(cubes,index,callback){            
            var yLine = new THREE.Vector3(0, 1, 0);

            var rotateCubes = cubes.filter((c)=>{
                return c.position.y == index * cubeSize
            });

            // console.log(rotateCubes);

            //一次转到位
            rotateCube(rotateCubes, yLine, 90 , 10,callback);
        }

        function rotateByAxisZ(cubes,index,callback){            
            var zLine = new THREE.Vector3(0, 0, 1);

            var rotateCubes = cubes.filter((c)=>{
                return c.position.z == index * cubeSize
            });

            // console.log(rotateCubes);

            //一次转到位
            rotateCube(rotateCubes, zLine, 90 , 10,callback);
        }

        /**
         * 随机旋转
         **/
        function disorganize(){
            console.log("disorganize");

            var group = scene.children.filter(item=>{
                return item.name=="cubeGroup";
            });

            var cubes = group[0].children.filter(cube=>{
                return cube.name == "cube";
            });

            //三个面xLineAd,zLine,yLine

            //改为链式调用
            var promises = [];

            for(var i=0;i<10;i++){
                promises.push(()=>{
                        return new Promise((resolve,reject)=>{                            
                            var index = Math.floor(Math.random()*3);

                            if(index==0){
                                rotateByAxisX(cubes,Math.floor(Math.random()*3) - 1,()=>{
                                    console.log("绕x轴旋转完毕");
                                    resolve();
                                });
                            }
                            else if(index==1){
                                rotateByAxisY(cubes,Math.floor(Math.random()*3) - 1,()=>{
                                    console.log("绕y轴旋转完毕");
                                    resolve();
                                });
                            }
                            else{
                                rotateByAxisZ(cubes,Math.floor(Math.random()*3) - 1,()=>{
                                    console.log("绕z轴旋转完毕");
                                    resolve();
                                });
                            }
                        });
                });
            }

            runPromiseByQueue(promises);
        }

        
        function runPromiseByQueue(myPromises) {
            myPromises.reduce(
                (prev, cur) => {
                    return prev.then(()=>{
                        return cur();
                    });
                }
            ,Promise.resolve())
        }

        /**
         * 还原
         **/
        function resetScene(){
            // alert("reset");

            var group = scene.children.filter(item=>{
                return item.name=="cubeGroup";
            });

            var cubes = group[0].children.filter(cube=>{
                return cube.name == "cube";
            });

            cubes.forEach(cube=>{
                var matrix = cube.matrix.clone();
                matrix.getInverse(matrix);
                
                //主要是因为创建这 27 个小方块时默认其中心都在父元素自身坐标系的坐标原点
                //对所有小方块做逆反矩阵变换自然都会回到初始位置
                cube.applyMatrix(matrix);
                
                //所有小方块中心重新设置为初始中心位置
                cube.position.x = cube.originPosition.x;
                cube.position.y = cube.originPosition.y;
                cube.position.z = cube.originPosition.z;
            });

            render();
        }

        var sceneMemory = {};
        var miniGroup = null;

        function saveScene(){
            console.log("saveScene");

            var group = scene.children.filter(item=>{
                return item.name=="cubeGroup";
            });

            var cubes = group[0].children.filter(cube=>{
                return cube.name == "cube";
            });

            if(miniGroup!=null){
                scene.remove(miniGroup);
            }

            miniGroup = createCubeGroup("miniGroup","miniCube",cubeSize);

            scene.add(miniGroup);

            //利用两边group添加cube时序号一致
            for(var i=0;i<cubes.length;i++){
                var cube = cubes[i];

                var mat = cube.matrix.clone();                
                sceneMemory[cube.id] = mat;

                var miniCube = miniGroup.children[i];
                var matrix = miniCube.matrix.clone();
                matrix.getInverse(matrix);

                miniCube.applyMatrix(matrix);
                miniCube.applyMatrix(mat);
            }

            
            miniGroup.rotateY(45 / 180 * Math.PI);

            //这里axis是group自有坐标系
            // miniGroup.rotateOnAxis(new THREE.Vector3(1, 0, 1), 45 / 180 * Math.PI);

            miniGroup.position.x = -originWidth/2  + 50;
            miniGroup.position.y = -originHeight/2 + 50;
            miniGroup.scale.set(0.2, 0.2, 0.2);
            
            scene.add(miniGroup);

            render();

            console.log("已保存!");
        }

        function restoreScene(){
            console.log("restoreScene");

            if(miniGroup!=null) {
                var miniCubes = miniGroup.children;

                var group = scene.children.filter(item=>{
                    return item.name=="cubeGroup";
                })[0];

                //利用两边group添加cube时序号一致
                for(var i=0;i<miniCubes.length;i++){
                    var miniCube = miniCubes[i];

                    var mat = miniCube.matrix.clone();
                    var cube = group.children[i];

                    var matrix = cube.matrix.clone();
                    matrix.getInverse(matrix);

                    //使用逆矩阵还原
                    cube.applyMatrix(matrix);

                    //使用小方块的矩阵
                    cube.applyMatrix(mat);
                }

                render();
            }
        }
    }